/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2005
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
* 
* Author's email :    insane@dees.ufmg.br
* Author's website :  http://www.dees.ufmg.br/insane
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
package br.ufmg.dees.insane.model.disc.element;

import java.util.ArrayList;
import java.util.ListIterator;

import br.ufmg.dees.insane.crossSection.CrossSection;
import br.ufmg.dees.insane.materialMedia.material.Material;
import br.ufmg.dees.insane.model.disc.analysismodel.AnalysisModel;
import br.ufmg.dees.insane.model.disc.element.force.ElementForce;
import br.ufmg.dees.insane.model.disc.integrationpoint.IntegrationPoint;
import br.ufmg.dees.insane.model.disc.node.Node;
import br.ufmg.dees.insane.model.disc.pointforce.PointForce;
import br.ufmg.dees.insane.model.disc.shape.Shape;
import br.ufmg.dees.insane.util.IMatrix;
import br.ufmg.dees.insane.util.IVector;


/**
*A class representing an element.
*
*@author Fonseca, Flavio & Pitangueira, Roque
*@version 1.0
*@since September 2003
*@see Node
*@see Material
*@see AnalysisModel
*@see PointForce
*@see ElementForce
*@see Shape
*/

public abstract class Element implements java.io.Serializable {
	
	/**The label of this Element.*/
	protected String label;
	
	/**The type of this Element.*/
	private char[] type = new char[2];
	
	/**The incidence of this Element.*/
	protected ArrayList<Node> incidence = new ArrayList<Node>();
	
	/**The elementForces on this Element.*/
	protected ArrayList<ElementForce> elmLineForces = new ArrayList<ElementForce>();
	
	/**The PointForces on this Element.*/
	protected ArrayList<PointForce> elmPointForces = new ArrayList<PointForce>();
	
	/**The surfaceForces on this Element.*/
	protected ArrayList<ElementForce> elmSurfaceForces = new ArrayList<ElementForce>();
	
	/**The volumeForces on this Element.*/
	protected ArrayList<ElementForce> elmVolumeForces = new ArrayList<ElementForce>();
	
	/**The Material of this Element.*/
	protected Material myMaterial;
	
	/**The AnalysisModel of this Element.*/
	protected AnalysisModel myAnalysisModel;
	
	/**The Shape of this Element.*/
	protected Shape myShape;
	
	/**The CrossSection of this Element.*/
	protected CrossSection myCrossSection;
	
	/**The equations of this Element.<br>
	*It is an array with all the possible equations for an Element.<br>
	*A zero indicates that this equation is not valid for this Element's AnalysisModel.<br>
	*A negative number indicates that there is a restraint for the displacement related to that equation.<br>
	*A positive number indicates that this displacement may occur.
	*/
	protected int[] equations;
	
	/**The reduced equations of this Element.<br>
	*It is an array with the valid equations for this Element's AnalysisModel.<br>
	*A negative number indicates that there is a restraint for the displacement related to that equation.<br>
	*A positive number indicates that this displacement may occur.
	*/
	protected int[] reducedEquations;
	
	/** The Integration Points of this Element. */
    protected ArrayList integrationPoints = new ArrayList();
	
//********************************************************************************
	
	/** Returns the label of this Element.
	*@return The label of this Element.
	*/
	public String getLabel(){
		return (label);
	};
	
	/** Sets the label of this Element.
	*@param a The label of this Element.
	*/
	public void setLabel(String a){
		label = a;
	};
	
//*****************************************************************************
	
	/** Returns the type of this Element.
	*@return The type of this Element.
	*/
	public char getType(int index) {
		return type[index];
	}
	
	/** Sets the type of this Element.
	*@param a The first type of this Element.
	*@param a The second type of this Element.
	*/
	public void setType(char a, char b) {
		this.type[0] = a;
		this.type[1] = b;
	}
	
//********************************************************************************
	
	/** Returns the equation of this Element in the specified position of the equations array.
	*@param a The position of the equation desired.
	*@return The equation of this Element in the specified position of the equations array.
	*/
	public int getEquation(int a) {
		return (equations[a]);
	};
	
	/** Sets the equation of this Element in the specified position of the equations array.
	*@param a The position of the equation desired.
	*@param b The equation of this Element in the specified position of the equations array.
	*/
	public void setEquation(int a, int b) {
		equations[a] = b;
	};
	
//********************************************************************************
	
	/** Returns the equation of this Element in the specified position of the reduced equations array.
	*@param a The position of the equation desired.
	*@return The equation of this Element in the specified position of the reduced equations array
	*/
	public int getReducedEquation(int a) {
		return (reducedEquations[a]);
	};
	
	public int[] getReducedEquation() {
		return reducedEquations;
	};
	
	/** Sets the reduced equations array of this Element.
	*@param a The reduced equations array of this Element.
	*/
	public void setReducedEquations(int[] a) {
		reducedEquations = a;
	};
	
//********************************************************************************
	
	/** Returns the Material of this Element.
	*@return The Material of this Element.
	*/
	public Material getMaterial() {
		return (myMaterial);
	};
	
	/** Sets the Material of this Element.
	*@param a The Material of this Element.
	*/
	public void setMaterial(Material a) {
		myMaterial = a;
	};
	
//********************************************************************************
	
	/** Returns the AnalysisModel of this Element.
	*@return The AnalysisModel of this Element.
	*/
	public AnalysisModel getAnalysisModel() {
		return (myAnalysisModel);
	};
	
	/** Sets the AnalysisModel of this Element.
	*@param a The AnalysisModel of this Element.
	*/
	public void setAnalysisModel(AnalysisModel a) {
		myAnalysisModel = a;
	};
	
//********************************************************************************
	
	/** Returns the Shape of this Element.
	*@return The Shape of this Element.
	*/
	public Shape getShape() {
		return (myShape);
	};
	
	/** Sets the Shape of this Element.
	*@param a The Shape of this Element.
	*/
	public void setShape(Shape a) {
		myShape = a;
	};
	
//********************************************************************************
	
	/** Returns the CrossSection of this FrameElement.
	*@return The CrossSection of this FrameElement.
	*/
	public CrossSection getCrossSection() {
		return (myCrossSection);
	};
	
	/** Sets the CrossSection of this FrameElement.
	*@param a The CrossSection of this FrameElement.
	*/
	public void setCrossSection(CrossSection a) {
		myCrossSection = a;
	};
	
//********************************************************************************
	
	/** Returns the incidence of this Element.
	*@return The incidence of this Element.
	*/
	public ArrayList getIncidence() {
		return (incidence);
	};
	
	/** Sets the incidence of this Element.
	*@param a The incidence of this Element.
	*/
	public void setIncidence(ArrayList<Node> a) {
		incidence = a;
	};
	
	/** Returns the Node in the specified position of the incidence of this Element.
	*@param a The position of the desired Node in the incidence.
	*@return The Node in the specified position of the incidence of this Element.
	*/
	public Node getNode(int a) {
		return ((Node) incidence.get(a));
	};
	
	/** Returns the Node with the specified label.
	*@param a The label of the desired Node.
	*@return The Node with the specified label.
	*/
	public Node getNode(String a) {
		Node node;
		ListIterator nodes = incidence.listIterator();
		while (nodes.hasNext()) {
			node = (Node) nodes.next();
			if (node.getLabel() == a) {
				return (node);
			};
		};
		return (null);
	};
	
	/** Adds the specified Node to the incidence.
	*@param a The Node to be added to the incidence.
	*/
	public void add(Node a) {
		incidence.add(a);
	};
	
//********************************************************************************
	
	/** Returns the ElmLineForcesList of this Element.
	*@return The ElmLineForcesList of this Element.
	*/
	public ArrayList getElmLineForcesList() {
		return (elmLineForces);
	};
	
	/** Sets the ElmLineForcesList of this Element.
	*@param a The ElmLineForcesList of this Element.
	*/
	public void setElmLineForcesList(ArrayList<ElementForce> a) {
		elmLineForces = a;
	};
	
	/** Returns the ElementForce in the specified position of the ElmLineForcesList of this Element.
	*@param a The position of the ElementForce desired in the ElmLineForcesList.
	*@return The ElementForce in the specified position of the ElmLineForcesList of this Element.
	*/
	public ElementForce getElementForce(int a) {
		return ((ElementForce) elmLineForces.get(a));
	};
	
	/** Returns the ElementForce with the specified label.
	*@param a The label of the ElementForce desired.
	*@return The ElementForce with the specified label.
	*/
	public ElementForce getElementForce(String a) {
		ElementForce ef;
		ListIterator efs = elmLineForces.listIterator();
		while (efs.hasNext()) {
			ef = (ElementForce) efs.next();
			if (ef.getLabel() == a) {
				return (ef);
			};
		};
		return (null);
	};
	
	/** Adds the specified ElementForce to the ElmLineForcesList.
	*@param a The ElementForce to be added to the ElmLineForcesList.
	*/
	public void add(ElementForce a) {
		elmLineForces.add(a);
	};
	
//********************************************************************************
	
	/** Returns the ElmPointForcesList of this Element.
	*@return The ElmPointForcesList of this Element.
	*/
	public ArrayList getElmPointForcesList() {
		return (elmPointForces);
	};
	
	/** Sets the ElmPointForcesList of this Element.
	*@param a The ElmPointForcesList of this Element.
	*/
	public void setElmPointForcesList(ArrayList<PointForce> a) {
		elmPointForces = a;
	};
	
	/** Returns the PointForce on this Element in the specified position of the ElmPointForcesList of this Element.
	*@param a The position of the PointForce desired in the ElmPointForcesList.
	*@return The PointForce on this Element in the specified position of the ElmPointForcesList of this Element.
	*/
	public PointForce getElmPointForce(int a) {
		return ((PointForce) elmPointForces.get(a));
	};
	
	/** Returns the PointForce on this Element with the specified label.
	*@param a The PointForce on this Element with the specified label.
	*@return The PointForce on this Element with the specified label.
	*/
	public PointForce getElmPointForce(String a) {
		PointForce pf;
		ListIterator pfs = elmPointForces.listIterator();
		while (pfs.hasNext()) {
			pf = (PointForce) pfs.next();
			if (pf.getLabel() == a) {
				return (pf);
			};
		};
		return (null);
	};
	
	/** Adds the specified BodyForce to the BodyForcesList.
	*@param a The BodyForce to be added to the BodyForcesList.
	*/
	public void addElmPointForce(PointForce a)
	{
		elmPointForces.add(a);
	}
	
	/** Adds the specified PointForce to the elmPointForcesList.
	*@param a The PointForce to be added to the elmPointForcesList.
	*/
	public void add(PointForce a) {
		elmPointForces.add(a);
	};
	
//********************************************************************************
	
	/** Returns the ElementForce in the specified position of the ElmLineForcesList of this Element.
	*@param a The position of the ElementForce desired in the ElmLineForcesList.
	*@return The ElementForce in the specified position of the ElmLineForcesList of this Element.
	*/
	public ElementForce getElmLineElementForce(int a)	{
		return ((ElementForce) elmLineForces.get(a));
	}
	
	/** Returns the ElementForce with the specified label.
	*@param a The label of the ElementForce desired.
	*@return The ElementForce with the specified label.
	*/
	protected ElementForce getElmLineElementForce(String a){
		ElementForce ef;
		ListIterator efs = elmLineForces.listIterator();
		while (efs.hasNext()){
			ef = (ElementForce) efs.next();
			if (ef.getLabel() == a)	{
				return (ef);
			}
		}
		return (null);
	}
	
	/** Adds the specified ElementForce to the ElmLineForcesList.
	*@param a The ElementForce to be added to the ElmLineForcesList.
	*/
	public void addElmLineForce(ElementForce a) {
		elmLineForces.add(a);
	}
	
//********************************************************************************
	
	/** Returns the elmSurfaceForcesList of this Element.
	*@return The elmSurfaceForcesList of this Element.
	*/
	public ArrayList getElmSurfaceForcesList() {
		return (elmSurfaceForces);
	}
	
	/** Sets the ElmSurfaceForcesList of this Element.
	*@param a The ElmSurfaceForcesList of this Element.
	*/
	protected void setElmSurfaceForcesList(ArrayList<ElementForce> a){
		elmSurfaceForces = a;
	}
	
	/** Returns the ElementForce in the specified position of the ElmSurfaceForcesList of this Element.
	*@param a The position of the ElementForce desired in the ElmSurfaceForcesList.
	*@return The ElementForce in the specified position of the ElmSurfaceForcesList of this Element.
	*/
	protected ElementForce getElmSurfaceElementForce(int a){
		return ((ElementForce) elmSurfaceForces.get(a));
	}
	
	/** Returns the ElementForce with the specified label.
	*@param a The label of the ElementForce desired.
	*@return The ElementForce with the specified label.
	*/
	protected ElementForce getElmSurfaceElementForce(String a){
		ElementForce ef;
		ListIterator efs = elmSurfaceForces.listIterator();
		while (efs.hasNext()){
			ef = (ElementForce) efs.next();
			if (ef.getLabel() == a)	{
				return (ef);
			}
		}
		return (null);
	}
	
	/** Adds the specified ElementForce to the ElmSurfaceForcesList.
	*@param a The ElementForce to be added to the ElmSurfaceForcesList.
	*/
	public void addElmSurfaceForce(ElementForce a) {
		elmSurfaceForces.add(a);
	}
	
//********************************************************************************
	
	/** Returns the elmVolumeForcesList of this Element.
	*@return The elmVolumeForcesList of this Element.
	*/
	public ArrayList getElmVolumeForcesList() {
		return (elmVolumeForces);
	}
	
	/** Sets the ElmVolumeForcesList of this Element.
	*@param a The ElmVolumeForcesList of this Element.
	*/
	protected void setElmVolumeForcesList(ArrayList<ElementForce> a) {
		elmVolumeForces = a;
	}
	
	/** Returns the ElementForce in the specified position of the ElmVolumeForcesList of this Element.
	*@param a The position of the ElementForce desired in the ElmVolumeForcesList.
	*@return The ElementForce in the specified position of the ElmVolumeForcesList of this Element.
	*/
	protected ElementForce getElmVolumeElementForce(int a) {
		return ((ElementForce) elmVolumeForces.get(a));
	}
	
	/** Returns the ElementForce with the specified label.
	*@param a The label of the ElementForce desired.
	*@return The ElementForce with the specified label.
	*/
	protected ElementForce getElmVolumeElementForce(String a) {
		ElementForce ef;
		ListIterator efs = elmVolumeForces.listIterator();
		while (efs.hasNext()) {
			ef = (ElementForce) efs.next();
			if (ef.getLabel() == a)	{
				return (ef);
			}
		}
		return (null);
	}
	
	/** Adds the specified ElementForce to the ElmVolumeForcesList.
	*@param a The ElementForce to be added to the ElmVolumeForcesList.
	*/
	public void addElmVolumeForce(ElementForce a) {
		elmVolumeForces.add(a);
	}
//  ********************************************************************************
    public void updateState(){
        ListIterator ips = integrationPoints.listIterator();
        while(ips.hasNext()) {
            IntegrationPoint integrationPoint = (IntegrationPoint) ips.next();
            integrationPoint.updateState();
        }
    }	
//********************************************************************************
	
	/** Returns the stiffness matrix of this Element in the global coords.
	*@return The stiffness matrix of this Element in the global coords.
	*/
	public abstract IMatrix globalStiffnessMatrix();
	
	/** Returns the reduced equivalent nodal force vector of this Element in the global coords.
	*@return The reduced equivalent nodal force vector of this Element in the global coords.
	*/
	public abstract IVector globalEquivalentForceVector();
	
	/** Calculates the internal forces of this Element.<br>
	*The displacements of the Nodes must have been previously calculated.
	*/
	public abstract IVector internalForces();
	
//********************************************************************************
	
	/** Sets to zero all the results calculated. */
	public void cleanResults() {}
	
//********************************************************************************
	

	/** Returns the reduce stiffness matrix of this FrameElement in the global coords.
	*@return The reduce stiffness matrix of this FrameElement in the global coords.
	*/
	public IMatrix reduceStiffnessMatrix(){
	    int neq = this.getNumberOfReducedEquations();
	    System.out.println("Neq ="+neq);
		IMatrix k = new IMatrix(neq, neq);
		IMatrix globlalK = this.globalStiffnessMatrix();
		int linha =0;
		for (int i=0; i<globlalK.getNumRow(); i++){
		    if(this.getReducedEquation(i) > 0){
		        linha++;
		        int coluna = 0;
		        for (int j=0; j<globlalK.getNumCol(); j++){
		            if(this.getReducedEquation(j) > 0){
		                coluna++;
			            k.setElement(linha-1, coluna-1, globlalK.getElement(i,j));
		            }
		        }
		    }
		}
		return k;
	};
	
	
	/** Returns the reduce stiffness matrix of this FrameElement in the global coords.
	*@return The reduce stiffness matrix of this FrameElement in the global coords.
	*/
	public int getNumberOfReducedEquations(){
	    int neq = 0;
		for (int i=0; i<this.reducedEquations.length; i++){
			if(this.getReducedEquation(i) > 0) neq ++;
		}
		return neq;
	};
//********************************************************************************
	/** Returns the reduce force vector of this FrameElement in the global coords.
	*@return The reduce force vector of this FrameElement in the global coords.
	*/
	public IVector reduceEquivalentForceVector(){
		int neq = this.getNumberOfReducedEquations();
	    System.out.println("Neq ="+neq);
		IVector f = new IVector(neq);
		IVector globlalF = this.globalEquivalentForceVector();
		int linha =0;
		for (int i=0; i<globlalF.getSize(); i++){
		    if(this.getReducedEquation(i) > 0){
		        linha++;
			       f.setElement(linha-1, globlalF.getElement(i));
		            }
		        }
		return f;
	};
	
//********************************************************************************
	
}
